package com.br3akr.rollerServer.engine;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Observable;
import java.util.Observer;
import java.util.Properties;
import java.util.Random;
import java.util.Set;

import com.br3akr.rollerTransferProtocol.transferProtocol.FromClient;
import com.br3akr.rollerTransferProtocol.transferProtocol.FromServer;
import com.br3akr.rollerTransferProtocol.transferProtocol.PlayerEntry;
import com.br3akr.rollerTransferProtocol.transferProtocol.PlayerState;

public class ServerThread extends Thread implements Observer {
    private Socket socket;
	private ObjectOutputStream out;
	private ObjectInputStream in;
	private Session session;
	private int rollMethod;
	private boolean stopped = false;
	public final static int TWO_D_SIX_SUBTRACT = 0;
	public final static int TWO_D_SIX_LOWEST = 1;
	public final static int FUDGE_DICE = 2;
    
    public ServerThread(Socket socket) {
    	super("ServerThread");
    	this.socket = socket;
		try {
			Properties prop = new Properties();
			prop.load(this.getClass().getClassLoader()  
				.getResourceAsStream("config/app/config.properties"));  
			String rollMethodValue = (String)prop.getProperty("rollMethod");
			rollMethod = new Integer(rollMethodValue).intValue();
			System.out.println("Roll Method: " + rollMethod);
		} catch (Exception e) {
			e.printStackTrace();
		}
		Server.getRoom().addObserver(this);
    }
    
	public void update(Observable o, Object arg) {
		if (stopped) {
			return;
		}
		try {
			out.writeObject(makeFromServer(session, 
					Server.getRoom().getMessage(), 
					Server.getRoom().getSessions()));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
    public void run() {
    	if (stopped) {
    		return;    		
    	}
		try {
		    out = new ObjectOutputStream(socket.getOutputStream());
		    in = new ObjectInputStream(
					    socket.getInputStream());
            FromClient fromClient = null;
	    	while ((fromClient = (FromClient)in.readObject()) != null) {
				if (fromClient.getActionType() == FromClient.ActionType.CONNECT) {
					System.out.println("Client called connect.");
					String name = (String)fromClient.getActionParameters()[0];
					boolean gm = (Boolean)fromClient.getActionParameters()[1];
					session = new Session(name, gm);
					Server.getRoom().addSession(session);
					Server.getRoom().setMessage(session.getPlayer() 
							+ " has joined the room.");
				} else if (fromClient.getActionType() == FromClient.ActionType.ROLL) {
					System.out.println("Client called roll.");
					Random r = new Random();
					int plus = r.nextInt(6) + 1;
					int minus = r.nextInt(6) + 1;
					StringBuilder sb = new StringBuilder();
					sb.append(session.getPlayer() + " Rolled ");
					int roll = 0;
					
					switch(rollMethod) {
						case TWO_D_SIX_SUBTRACT:
							roll = plus - minus;
							sb.append("Plus(" + plus + "), Minus(" + minus + "): " + roll);
						break;
						case TWO_D_SIX_LOWEST:
							if (plus > minus) {
								roll = -minus;
							} else if (plus < minus) {
								roll = plus;
							}
							sb.append("Plus(" + plus + "), Minus(" + minus + "): " + roll);
						break;
						case FUDGE_DICE:
							for (int i = 0; i < 4; i++) {
								int a = r.nextInt(3) - 1;
								if (a == 1) {
									sb.append("(+)");
								} else if (a == -1) {
									sb.append("(-)");
								} else {
									sb.append("( )");
								}
								roll = roll + a;
							}
							sb.append(": " + roll);
						break;
					}
					Server.getRoom().setMessage(sb.toString());
				} else if (fromClient.getActionType() == FromClient.ActionType.MESSAGE) {
					Server.getRoom().setMessage("{{" + session.getUserId() 
							+ session.getPlayer() + "}}: " 
							+ ((String)fromClient.getActionParameters()[0]));
				} else if (fromClient.getActionType() == FromClient.ActionType.GRANT_FP) {
					System.out.println("Client called grant fp.");
					int fpGranted = ((Integer)fromClient.getActionParameters()[0]).intValue();
					String name = (String)fromClient.getActionParameters()[1];
					boolean successful = false;
					if (session.isGM()) {
						for (Session s : Server.getRoom().getSessions()) {
							if (s.getPlayer().equals(name)) {
								s.setFate(s.getFate() + fpGranted);
								successful = true;
							}
						}
						if (successful) {
							Server.getRoom().setMessage(session.getPlayer() 
									+ " grants " 
									+ name + " " 
									+ fpGranted 
									+ " fate point(s).");
						} else {
							out.writeObject(makeFromServer(session, "Could not find " 
									+ name + " to grant fate.", 
									Server.getRoom().getSessions()));
						}
					} else {
						out.writeObject(makeFromServer(session, 
								"You must be a GM in order to grant a fate point", 
								Server.getRoom().getSessions()));
					}
				} else if (fromClient.getActionType() == FromClient.ActionType.SPEND_FP) {
					if (session.getFate() > 0) {
						session.setFate(session.getFate() - 1);
						Server.getRoom().setMessage(session.getPlayer() 
								+ " has spent a fate! They have " + session.getFate() 
								+ " fate remaining.");
					} else {
						out.writeObject(makeFromServer(session, 
								"You do not have any fate points left.", 
								Server.getRoom().getSessions()));
					}
				} else if (fromClient.getActionType() == FromClient.ActionType.TYPED) {
					session.setTyping(true);
					Server.getRoom().updateAll();
					out.writeObject(makeFromServer(session, null, 
							Server.getRoom().getSessions()));
				} else if (fromClient.getActionType() == FromClient.ActionType.CLEARED_TEXT) {
					session.setTyping(false);
					Server.getRoom().updateAll();
				}
	    	}
		} catch (Exception e) {
			Server.getRoom().removePlayer(session.getPlayer());
			Server.getRoom().deleteObserver(this);
			Server.getRoom().setMessage(session.getPlayer() + " has left the room.");
			e.printStackTrace();
		    System.out.println("Lost connection to: " + session.getPlayer() 
		    		+ ". They are no longer observing the room.");
		} finally {
			if (out != null) {
				try { out.close(); } catch (IOException e) {}
			}
			if (in != null) {
				try { in.close(); } catch (Exception e) { }
			}
			if (socket != null) {
				try { socket.close(); } catch (Exception e) { }
			}
			stopped = true;
		}
    }
	
	private FromServer makeFromServer(Session session, String message, Set<Session> sessions) {
		PlayerState ps = new PlayerState(session.getPlayer(), session.getFate());
		PlayerEntry[] players = new PlayerEntry[sessions.size()];
		int i = 0;
		for (Session s : sessions) {
			players[i++] = new PlayerEntry(s.getPlayer(), s.isGM(), s.isTyping());
		}
		FromServer fs = new FromServer(message, ps, players, session.getUserId());
		return fs;
	}
}